#!/usr/bin/python
#-*- coding:utf-8 -*-

# ValueWheel.py
#
# Copyright (c) 2007 Marcelo Lira dos Santos
#
# Author: Marcelo Lira dos Santos <setanta@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA

import pygtk
pygtk.require('2.0')
import gtk
import gobject
import cairo
import math

class ValueWheel(gtk.DrawingArea):
	INT = 0
	AUR = 1
	CAR = 2
	FOR = 3
	FIS = 4
	AGI = 5
	PER = 6
	
	__gsignals__ = {
							'realize': 'override',
							'expose-event' : 'override',
							'button-press-event' : 'override',
							'button-release-event' : 'override',
							'motion-notify-event' : 'override',
						}

	def __init__(self, total_points=15, value_range=(-4, 6)):
		gtk.DrawingArea.__init__(self)
		self.add_events(gtk.gdk.BUTTON_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK |
				gtk.gdk.BUTTON_RELEASE_MASK)
		self.drag = False
		self.attribute = 0

		self.modifiers = [0, 0, 0, -2, 1, 2, 1]
		self.values = [0, 0, 0, 0, 0, 0, 0]
		self.labels = ['Int', 'Aur', 'Car', 'For', 'Fís', 'Agi', 'Per']
		#self.modifLabels = ['(%+d)' % modif for modif in self.modifiers]

		self.label_radius = 20.0
		self.min_value, self.max_value = value_range
		self.total_points = total_points
		
		gobject.signal_new('value-changed', ValueWheel, 
									gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
									(gobject.TYPE_PYOBJECT,))

	def do_realize(self):
		self.set_flags(self.flags() | gtk.REALIZED)

		events = gtk.gdk.EXPOSURE_MASK | gtk.gdk.BUTTON_PRESS_MASK \
					| gtk.gdk.POINTER_MOTION_MASK

		self.window = gtk.gdk.Window(self.get_parent_window(),
												x=self.allocation.x,
												y=self.allocation.y,
												width=self.allocation.width,
												height=self.allocation.height,
												window_type=gtk.gdk.WINDOW_CHILD,
												wclass=gtk.gdk.INPUT_OUTPUT,
												visual=self.get_visual(),
												colormap=self.get_colormap(),
												event_mask=self.get_events() | events)

		self.window.set_user_data(self)
		self.style.attach(self.window)
		self.style.set_background(self.window, gtk.STATE_NORMAL)


	def do_expose_event(self, event):
		context = self.window.cairo_create()
		context.rectangle(event.area.x, event.area.y,
							event.area.width, event.area.height)
		context.clip()
		
		self.draw(context)

		return False


	def do_button_press_event(self, event):
		rect = self.get_allocation()
		x = rect.x + rect.width / 2
		y = rect.y + rect.height / 2
		radius = min(rect.width / 2, rect.height / 2) - 50

		if event.type == gtk.gdk.BUTTON_PRESS:
			for i in xrange(0, 7):
				distance = (radius / (self.max_value - self.min_value)) * \
							(self.values[i] + self.modifiers[i] - self.min_value)
				cx = x + distance * math.cos(i * math.pi / 3.5)
				cy = y + distance * math.sin(i * math.pi / 3.5)
				if math.hypot(cx - event.x, cy - event.y) < 4:
					self.drag = True
					self.attribute = i
					return True

		elif event.type == gtk.gdk.SCROLL:
			print 'scroll'

		return False

	def do_button_release_event(self, event):
		self.drag = False
		return False

	def do_motion_notify_event(self, event):
		if self.drag:
			rect = self.get_allocation()
			x = rect.x + rect.width / 2
			y = rect.y + rect.height / 2

			radius = min(rect.width / 2, rect.height / 2) - 50
			distance = math.hypot(x - event.x, y - event.y)

			segment = radius / (self.max_value - self.min_value)
			point = int(round((distance / segment)) + self.min_value)

			if not self.values[self.attribute] == point:
				if point > self.modifiers[self.attribute] + 4 or \
					point < self.modifiers[self.attribute] - 2 or \
					point == self.values[self.attribute] + \
					self.modifiers[self.attribute]:
					return

				self.values[self.attribute] = point - self.modifiers[self.attribute]
				self.emit('value-changed', self.values)

				alloc = self.get_allocation()
				rect = gtk.gdk.Rectangle(0, 0, alloc.width, alloc.height)
				self.window.invalidate_rect(rect, True)
				self.window.process_updates(True)

		return False
		
	def reset_values(self):
		self.values = []

	def draw(self, context, redraw=False):
		rect = self.get_allocation()
		x = rect.x + rect.width / 2
		y = rect.y + rect.height / 2
		radius = min(rect.width / 2, rect.height / 2) - 50
		external_radius = radius + self.label_radius - 4
		total = self.calc_total()
		r, g, b = self.calc_polygon_color(total)
		
		# Show points
		context.set_font_size (16.0)
		context.set_source_rgb(r, g, b)
		context.move_to(6, 20)
		context.show_text('Pontos: %.1f / %d' % (total, self.total_points))

		# wheel
		context.set_source_rgb(0.9, 0.9, 0.9)
		context.move_to(x + radius, y)
		for i in xrange(7):
			context.line_to(x + radius * math.cos(i * math.pi / 3.5),
									y + radius * math.sin(i * math.pi / 3.5))
		context.close_path()
		context.fill_preserve()

		context.set_source_rgb(0.7, 0.7, 0.7)		
		context.set_font_size (16.0)
		for i in xrange(7):
			context.move_to(x, y)
			context.line_to(x + radius * math.cos(i * math.pi / 3.5),
									y + radius * math.sin(i * math.pi / 3.5))

			context.move_to(x - 10 + external_radius * math.cos(i * math.pi / 3.5),
								y + 5 + external_radius * math.sin(i * math.pi / 3.5))
			context.show_text(self.labels[i])
		context.stroke()
		
		# polygon
		context.save()
		context.set_source_rgba(r, g, b, 0.4)
		context.new_path()
		for i in xrange(0, 7):
			distance = (radius / (self.max_value - self.min_value)) * \
						(self.values[i] + self.modifiers[i] - self.min_value)
			context.line_to(x + distance * math.cos(i * math.pi / 3.5),
							y + distance * math.sin(i * math.pi / 3.5))
		context.fill()
		context.restore()
		context.close_path()
		
		# dots
		context.set_source_rgba(r, g, b, 0.8)
		for i in xrange(0, 7):
			distance = (radius / (self.max_value - self.min_value)) * \
							(self.values[i] + self.modifiers[i] - self.min_value)
			context.arc(x + distance * math.cos(i * math.pi / 3.5),
							y + distance * math.sin(i * math.pi / 3.5),
							4.2, 0, 2 * math.pi)
			context.fill()
			if self.values[i] < 1:
				rad = radius + 2
			else:
				rad = radius - 10
			distance = 5 + (rad / (self.max_value - self.min_value)) * \
							(self.values[i] + self.modifiers[i] - self.min_value + 1)
			context.move_to(x - 5 + distance * math.cos((i + 0.15) * \
									math.pi / 3.5),
									y + 7 + distance * math.sin((i + 0.15) * \
									math.pi / 3.5))
			context.set_source_rgba(r, g, b, 1.0)
			context.show_text(str(self.values[i] + self.modifiers[i]))
			
		context.stroke()

	def calc_polygon_color(self, total):
		r, g, b = 0, 0, 0
		if total == self.total_points:
			r, g, b = 0.1, 0.4, 0.1
		elif total < self.total_points:
			b = 1.0
		else:
			r = 1.0
		return (r, g, b)

	def calc_total(self):
		total = sum([sum(range(1, value + 1))
							for value in self.values if value >= 0])
		total += sum([value * 0.5 for value in self.values if value < 0])
		return total

def teste(widget, data):
	print widget.labels
	print widget.values
	print widget.modifiers
	print 'data: ', data

if __name__ == '__main__':
	window = gtk.Window()
	window.set_title('ValueWheel')
	window.set_default_size(500, 400)
	wheel = ValueWheel(15, (-4, 6))
	window.add(wheel)
	window.connect('delete-event', gtk.main_quit)
	wheel.connect('value-changed', teste)
	window.show_all()
	gtk.main()
